/*
 * XQueryFrame.java
 */

package xq.ide.forms;

import java.awt.Color;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Map;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import org.apache.log4j.Logger;
import xq.api.XQRequest;
import xq.api.XQResponse;
import xq.ide.XQOutputHandler;
import xq.ide.OutputLevel;
import xq.ide.XQOptions;
import xq.ide.XQueryEditor;
import xq.swing.text.XQueryEditorKit;
import static xq.util.CommonUtil.empty;
import static xq.util.LoggerUtil.getClassLogger;

public class XQueryFrame extends javax.swing.JFrame {

	private static final long serialVersionUID = 3348523510853601965L;
	private static final Logger logger = getClassLogger();

	private final XQOutputHandler outputHandler;

	private File lastOpenDirectory;
	private File lastSaveDirectory;

	public XQueryFrame() {
		outputHandler = new XQFrameOutputHandler();
		initComponents();
		setLocationRelativeTo(null);
		setExtendedState(MAXIMIZED_BOTH);
		editorTabbedPane.openNewDocument("text/xquery");
		editorTabbedPane.getActiveDocumentEditor().requestFocusInWindow();
		fileChooser.addChoosableFileFilter(new FileFilter() {

			@Override
			public boolean accept(File f) {
				return f.isDirectory() || f.getName().endsWith(".xqy");
			}

			@Override
			public String getDescription() {
				return "XQuery Scripts (*.xqy)";
			}
		});
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				closeAllDocuments();
			}
		});
		lastOpenDirectory = XQOptions.getInstance().getPreference(
				"XQueryFrame.fileChooser.lastOpenDirectory",
				fileChooser.getCurrentDirectory());
		lastOpenDirectory = XQOptions.getInstance().getPreference(
				"XQueryFrame.fileChooser.lastSaveDirectory",
				fileChooser.getCurrentDirectory());
	}

	public XQOutputHandler getOutputHandler() {
		return outputHandler;
	}

	/*--------------------------------------------------------------------
	 * File Operations
	 *--------------------------------------------------------------------*/

	public File[] chooseFilesToOpen() {
		File[] results = null;
		fileChooser.setCurrentDirectory(lastOpenDirectory);
		fileChooser.setSelectedFile(null);
		fileChooser.setSelectedFiles(null);
		fileChooser.setDialogTitle("Open");
		fileChooser.setMultiSelectionEnabled(true);
		int option = fileChooser.showOpenDialog(this);
		if (option == JFileChooser.APPROVE_OPTION) {
			results = fileChooser.getSelectedFiles();
			lastOpenDirectory = fileChooser.getCurrentDirectory();
			XQOptions.getInstance().setPreference("XQueryFrame.fileChooser.lastOpenDirectory", lastOpenDirectory.getPath());
		}
		return results;
	}

	public File chooseFileToSave() {
		return chooseFileToSaveImpl("Save");
	}

	public File chooseFileToSaveAs() {
		return chooseFileToSaveImpl("Save As");
	}

	private File chooseFileToSaveImpl(String title) {
		File result = null;
		fileChooser.setCurrentDirectory(lastSaveDirectory);
		fileChooser.setSelectedFile(null);
		fileChooser.setSelectedFiles(null);
		fileChooser.setDialogTitle(title);
		fileChooser.setMultiSelectionEnabled(false);
		int option = fileChooser.showSaveDialog(this);
		if (option == JFileChooser.APPROVE_OPTION) {
			result = fileChooser.getSelectedFile();
			lastSaveDirectory = fileChooser.getCurrentDirectory();
			XQOptions.getInstance().setPreference("XQueryFrame.fileChooser.lastSaveDirectory", lastOpenDirectory.getPath());
		}
		return result;
	}

	public void openNewDocument() {
		editorTabbedPane.openNewDocument(XQueryEditorKit.XQUERY_CONTENT_TYPE);
	}

	public void openDocument() {
		File[] files = chooseFilesToOpen();
		if (!empty(files)) {
			for (File f : files) {
				try {
					editorTabbedPane.openDocument(XQueryEditorKit.XQUERY_CONTENT_TYPE, f);
				} catch (IOException ex) {
					logger.error("Failed to open " + f.getName(), ex);
					showErrorMessage("Failed to open {0}\nReason: {0}", f, ex.getMessage());
				}
			}
		}
	}

	public boolean saveDocument() {
		boolean saved;
		File f = editorTabbedPane.getActiveDocumentFile();
		if (f == null) {
			saved = saveDocumentAs();
		} else {
			try {
				editorTabbedPane.saveActiveDocument();
				saved = true;
			} catch (IOException ex) {
				logger.error("Failed to save " + f.getName(), ex);
				showErrorMessage("Failed to save {0}\nReason: {0}", f, ex.getMessage());
				saved = false;
			}
		}
		return saved;
	}

	public boolean saveDocumentAs() {
		boolean saved = false;
		File f = chooseFileToSaveAs();
		if (f != null) {
			try {
				editorTabbedPane.saveActiveDocument(f);
				saved = true;
			} catch (IOException ex) {
				logger.error("Failed to save " + f.getName(), ex);
				showErrorMessage("Failed to save {0}\nReason: {0}", f, ex.getMessage());
			}
		}
		return saved;
	}

	public void closeDocument() {
		if (editorTabbedPane.isActiveDocumentModified()) {
			File f = editorTabbedPane.getActiveDocumentFile();
			int option;
			if (f == null) {
				option = confirmYNC("File is modified. Save it?");
			} else {
				option = confirmYNC("File {0} is modified. Save it?", f.getName());
			}
			switch (option) {
				case JOptionPane.YES_OPTION:
					if (saveDocument()) {
						editorTabbedPane.closeActiveDocument();
					}
					break;
				case JOptionPane.NO_OPTION:
					editorTabbedPane.closeActiveDocument();
					break;
				case JOptionPane.CANCEL_OPTION:
				default:
					break;
			}
		} else {
			editorTabbedPane.closeActiveDocument();
		}
	}

	public void closeAllDocuments() {
		while (editorTabbedPane.getTabCount() > 0) {
			closeDocument();
		}
	}

	public void renameDocument() {
		File f = editorTabbedPane.getActiveDocumentFile();
		if (f == null) {
			saveDocumentAs();
		} else {
			String fileName = f.getName();
			String name;
			String ext;
			int extIndex = fileName.lastIndexOf('.');
			if (extIndex > 0 && extIndex < fileName.length() - 1) {
				name = fileName.substring(0, extIndex);
				ext = fileName.substring(extIndex);
			} else {
				name = fileName;
				ext = null;
			}
			String newName = JOptionPane.showInputDialog(this, "New Name", name);
			String newFileName;
			if (ext != null) {
				newFileName = newName + ext;
			} else {
				newFileName = newName;
			}
			File newFile = new File(f.getParent(), newFileName);
			if (f.renameTo(newFile)) {
				try {
					editorTabbedPane.saveActiveDocument(f);
				} catch (IOException ex) {
					logger.error("Failed to rename " + f.getName(), ex);
					showErrorMessage("Failed to rename {0}\nReason: {0}", f, ex.getMessage());
				}
			}
		}
	}

	public void revertDocument() {
		if (editorTabbedPane.isActiveDocumentModified()) {
			File f = editorTabbedPane.getActiveDocumentFile();
			if (f != null) {
				int option = confirmYN("File {0} is modified. Revert changes?", f.getName());
				if (option == JOptionPane.YES_OPTION) {
					try {
						editorTabbedPane.revertActiveDocument();
					} catch (IOException ex) {
						logger.error("Failed to revert " + f.getName(), ex);
						showErrorMessage("Failed to revert {0}\nReason: {0}", f, ex.getMessage());
					}
				}
			}
		}
	}

	/*--------------------------------------------------------------------
	 * Edit Operations
	 *--------------------------------------------------------------------*/

	public void undo() {
		editorTabbedPane.undo();
	}

	public void redo() {
		editorTabbedPane.redo();
	}

	/*--------------------------------------------------------------------
	 * Dialog Operations
	 *--------------------------------------------------------------------*/

	public void showOptionsDialog() {
		new OptionsDialog(this, true).setVisible(true);
	}

	public int promptForParameters(Map<String, String> parameters) {
		if (parameters.size() > 0) {
			File file = editorTabbedPane.getActiveDocumentEditor().getFile();
			String title;
			if (file == null) {
				title = "Input Variables";
			} else {
				title = "Input Variables for " + file.getName();
			}
			ParameterDialog dialog = new ParameterDialog(this, title, true);
			dialog.setParameters(parameters);
			dialog.setVisible(true);
			return dialog.getOption();
		} else {
			return ParameterDialog.APPROVE_OPTION;
		}
	}

	/*--------------------------------------------------------------------
	 * Private Operations
	 *--------------------------------------------------------------------*/

	private void showErrorMessage(String message, Object... messageParams) {
		message = MessageFormat.format(message, messageParams);
		JOptionPane.showMessageDialog(this, message, "Error", JOptionPane.ERROR_MESSAGE);
	}

	private int confirmYNC(String message, Object... messageParams) {
		message = MessageFormat.format(message, messageParams);
		return JOptionPane.showConfirmDialog(this, message, "Question", JOptionPane.YES_NO_CANCEL_OPTION);
	}

	private int confirmYN(String message, Object... messageParams) {
		message = MessageFormat.format(message, messageParams);
		return JOptionPane.showConfirmDialog(this, message, "Question", JOptionPane.YES_NO_OPTION);
	}

	private class XQFrameOutputHandler extends XQOutputHandler {

		@Override
		public void printMessage(OutputLevel level, String message) {
			Color color;
			switch (level) {
				case WARNING: color = Color.ORANGE; break;
				case ERROR: color = Color.RED; break;
				default: color = Color.BLACK; break;
			}
			outputTextComponent.setForeground(color);
			outputTextComponent.setText(message);
			outputTextComponent.setCaretPosition(0);
			sourceOutputTextComponent.setText("");
		}

		@Override
		public void printResponse(XQRequest request, XQResponse<String> response) {
			super.printResponse(request, response);
			sourceOutputTextComponent.setText(request.getXQuery().getQueryText());
			beanTree.setModel(((XQueryEditor) editorTabbedPane.getActiveDocumentEditor()).getXmlResultTreeModel());
		}
	}

	@SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        fileNewAction = new xq.ide.actions.file.FileNewAction();
        fileOpenAction = new xq.ide.actions.file.FileOpenAction();
        fileSaveAction = new xq.ide.actions.file.FileSaveAction();
        fileSaveAsAction = new xq.ide.actions.file.FileSaveAsAction();
        fileCloseAction = new xq.ide.actions.file.FileCloseAction();
        fileRenameAction = new xq.ide.actions.file.FileRenameAction();
        fileRevertAction = new xq.ide.actions.file.FileRevertAction();
        fileChooser = new javax.swing.JFileChooser();
        editRedoAction = new xq.ide.actions.edit.EditRedoAction();
        editUndoAction = new xq.ide.actions.edit.EditUndoAction();
        toolsOptionsAction = new xq.ide.actions.tools.ToolsOptionsAction();
        verticalSplitPane = new xq.swing.SplitPane();
        editorTabbedPane = new xq.swing.EditorTabbedPane();
        outputTabbedPane = new javax.swing.JTabbedPane();
        outputScrollPane = new javax.swing.JScrollPane();
        outputTextComponent = new javax.swing.JTextArea();
        sourceScrollPane = new javax.swing.JScrollPane();
        sourceOutputTextComponent = new javax.swing.JTextArea();
        beanTreeScrollPane = new javax.swing.JScrollPane();
        beanTree = new javax.swing.JTree();
        mainMenuBar = new javax.swing.JMenuBar();
        fileMenu = new javax.swing.JMenu();
        fileNewMenuItem = new javax.swing.JMenuItem();
        fileOpenMenuItem = new javax.swing.JMenuItem();
        fileSaveMenuItem = new javax.swing.JMenuItem();
        fileSaveAsMenuItem = new javax.swing.JMenuItem();
        fileCloseMenuItem = new javax.swing.JMenuItem();
        jSeparator1 = new javax.swing.JSeparator();
        fileRevertMenuItem = new javax.swing.JMenuItem();
        fileRenameMenuItem = new javax.swing.JMenuItem();
        editMenu = new javax.swing.JMenu();
        editUndoMenuItem = new javax.swing.JMenuItem();
        editRedoMenuItem = new javax.swing.JMenuItem();
        toolsMenu = new javax.swing.JMenu();
        optionsMenuItem = new javax.swing.JMenuItem();

        fileChooser.setAcceptAllFileFilterUsed(false);

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("xq/ide/forms/Bundle"); // NOI18N
        setTitle(bundle.getString("XQueryFrame.title")); // NOI18N

        verticalSplitPane.setDividerLocation(350);
        verticalSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
        verticalSplitPane.setResizeWeight(0.6667);
        verticalSplitPane.setLeftComponent(editorTabbedPane);

        outputTextComponent.setColumns(20);
        outputTextComponent.setEditable(false);
        outputTextComponent.setFont(new java.awt.Font("Monospaced", 0, 11)); // NOI18N
        outputTextComponent.setRows(5);
        outputScrollPane.setViewportView(outputTextComponent);

        outputTabbedPane.addTab(bundle.getString("XQueryFrame.outputScrollPane.TabConstraints.tabTitle"), outputScrollPane); // NOI18N

        sourceOutputTextComponent.setColumns(20);
        sourceOutputTextComponent.setEditable(false);
        sourceOutputTextComponent.setRows(5);
        sourceScrollPane.setViewportView(sourceOutputTextComponent);

        outputTabbedPane.addTab(bundle.getString("XQueryFrame.sourceScrollPane.TabConstraints.tabTitle"), sourceScrollPane); // NOI18N

        beanTree.setModel(null);
        beanTreeScrollPane.setViewportView(beanTree);

        outputTabbedPane.addTab(bundle.getString("XQueryFrame.beanTreeScrollPane.TabConstraints.tabTitle"), beanTreeScrollPane); // NOI18N

        verticalSplitPane.setRightComponent(outputTabbedPane);

        fileMenu.setMnemonic('F');
        fileMenu.setText(bundle.getString("XQueryFrame.fileMenu.text")); // NOI18N

        fileNewMenuItem.setAction(fileNewAction);
        fileNewMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_N, java.awt.event.InputEvent.CTRL_MASK));
        fileMenu.add(fileNewMenuItem);

        fileOpenMenuItem.setAction(fileOpenAction);
        fileOpenMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.event.InputEvent.CTRL_MASK));
        fileMenu.add(fileOpenMenuItem);

        fileSaveMenuItem.setAction(fileSaveAction);
        fileSaveMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK));
        fileMenu.add(fileSaveMenuItem);

        fileSaveAsMenuItem.setAction(fileSaveAsAction);
        fileSaveAsMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.ALT_MASK | java.awt.event.InputEvent.CTRL_MASK));
        fileMenu.add(fileSaveAsMenuItem);

        fileCloseMenuItem.setAction(fileCloseAction);
        fileCloseMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F4, java.awt.event.InputEvent.CTRL_MASK));
        fileMenu.add(fileCloseMenuItem);
        fileMenu.add(jSeparator1);

        fileRevertMenuItem.setAction(fileRevertAction);
        fileMenu.add(fileRevertMenuItem);

        fileRenameMenuItem.setAction(fileRenameAction);
        fileMenu.add(fileRenameMenuItem);

        mainMenuBar.add(fileMenu);

        editMenu.setMnemonic('E');
        editMenu.setText(bundle.getString("XQueryFrame.editMenu.text")); // NOI18N

        editUndoMenuItem.setAction(editUndoAction);
        editUndoMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, java.awt.event.InputEvent.CTRL_MASK));
        editMenu.add(editUndoMenuItem);

        editRedoMenuItem.setAction(editRedoAction);
        editRedoMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Y, java.awt.event.InputEvent.CTRL_MASK));
        editMenu.add(editRedoMenuItem);

        mainMenuBar.add(editMenu);

        toolsMenu.setMnemonic('T');
        toolsMenu.setText(bundle.getString("XQueryFrame.toolsMenu.text")); // NOI18N

        optionsMenuItem.setAction(toolsOptionsAction);
        toolsMenu.add(optionsMenuItem);

        mainMenuBar.add(toolsMenu);

        setJMenuBar(mainMenuBar);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(verticalSplitPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 676, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(verticalSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 522, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTree beanTree;
    private javax.swing.JScrollPane beanTreeScrollPane;
    private javax.swing.JMenu editMenu;
    private xq.ide.actions.edit.EditRedoAction editRedoAction;
    private javax.swing.JMenuItem editRedoMenuItem;
    private xq.ide.actions.edit.EditUndoAction editUndoAction;
    private javax.swing.JMenuItem editUndoMenuItem;
    xq.swing.EditorTabbedPane editorTabbedPane;
    javax.swing.JFileChooser fileChooser;
    private xq.ide.actions.file.FileCloseAction fileCloseAction;
    private javax.swing.JMenuItem fileCloseMenuItem;
    private javax.swing.JMenu fileMenu;
    private xq.ide.actions.file.FileNewAction fileNewAction;
    private javax.swing.JMenuItem fileNewMenuItem;
    private xq.ide.actions.file.FileOpenAction fileOpenAction;
    private javax.swing.JMenuItem fileOpenMenuItem;
    private xq.ide.actions.file.FileRenameAction fileRenameAction;
    private javax.swing.JMenuItem fileRenameMenuItem;
    private xq.ide.actions.file.FileRevertAction fileRevertAction;
    private javax.swing.JMenuItem fileRevertMenuItem;
    private xq.ide.actions.file.FileSaveAction fileSaveAction;
    private xq.ide.actions.file.FileSaveAsAction fileSaveAsAction;
    private javax.swing.JMenuItem fileSaveAsMenuItem;
    private javax.swing.JMenuItem fileSaveMenuItem;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JMenuBar mainMenuBar;
    private javax.swing.JMenuItem optionsMenuItem;
    private javax.swing.JScrollPane outputScrollPane;
    private javax.swing.JTabbedPane outputTabbedPane;
    javax.swing.JTextArea outputTextComponent;
    javax.swing.JTextArea sourceOutputTextComponent;
    private javax.swing.JScrollPane sourceScrollPane;
    private javax.swing.JMenu toolsMenu;
    private xq.ide.actions.tools.ToolsOptionsAction toolsOptionsAction;
    private xq.swing.SplitPane verticalSplitPane;
    // End of variables declaration//GEN-END:variables
}
